/*
 * Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>.
 * All rights reserved. Distributed under the terms of the MIT License.
 *
 * Copyright 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
 *
 *
 * Class scanline_storage_subpix, a slightly modified version of
 * scanline_storage customized to store 3 covers per pixel
 *
 */

#ifndef AGG_SCANLINE_STORAGE_SUBPIX_INCLUDED
#define AGG_SCANLINE_STORAGE_SUBPIX_INCLUDED

#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "agg_array.h"
#include "agg_scanline_storage_aa.h"


namespace agg
{
	//--------------------------------------------scanline_storage_subpix
	template<class T> class scanline_storage_subpix
	{
	public:
		typedef T cover_type;

		//---------------------------------------------------------------
		struct span_data
		{
			int32 x;
			int32 len;		 // If negative, it's a solid span, covers is valid
			int	  covers_id; // The index of the cells in the scanline_cell_storage
		};

		//---------------------------------------------------------------
		struct scanline_data
		{
			int		 y;
			unsigned num_spans;
			unsigned start_span;
		};


		//---------------------------------------------------------------
		class embedded_scanline
		{
		public:

			//-----------------------------------------------------------
			class const_iterator
			{
			public:
				struct span
				{
					int32	 x;
					int32	 len; // If negative, it's a solid span, covers is valid
					const T* covers;
				};

				const_iterator() : m_storage(0) {}
				const_iterator(const embedded_scanline& sl) :
					m_storage(sl.m_storage),
					m_span_idx(sl.m_scanline.start_span)
				{
					init_span();
				}

				const span& operator*()	 const { return m_span;	 }
				const span* operator->() const { return &m_span; }

				void operator ++ ()
				{
					++m_span_idx;
					init_span();
				}

			private:
				void init_span()
				{
					const span_data& s = m_storage->span_by_index(m_span_idx);
					m_span.x	  = s.x;
					m_span.len	  = s.len;
					m_span.covers = m_storage->covers_by_index(s.covers_id);
				}

				const scanline_storage_subpix* m_storage;
				unsigned				   m_span_idx;
				span					   m_span;
			};

			friend class const_iterator;


			//-----------------------------------------------------------
			embedded_scanline(const scanline_storage_subpix& storage) :
				m_storage(&storage)
			{
				init(0);
			}

			//-----------------------------------------------------------
			void	 reset(int, int)	 {}
			unsigned num_spans()   const { return m_scanline.num_spans;	 }
			int		 y()		   const { return m_scanline.y;			 }
			const_iterator begin() const { return const_iterator(*this); }

			//-----------------------------------------------------------
			void init(unsigned scanline_idx)
			{
				m_scanline_idx = scanline_idx;
				m_scanline = m_storage->scanline_by_index(m_scanline_idx);
			}

		private:
			const scanline_storage_subpix* m_storage;
			scanline_data			   m_scanline;
			unsigned				   m_scanline_idx;
		};


		//---------------------------------------------------------------
		scanline_storage_subpix() :
			m_covers(),
			m_spans(256-2),			// Block increment size
			m_scanlines(),
			m_min_x( 0x7FFFFFFF),
			m_min_y( 0x7FFFFFFF),
			m_max_x(-0x7FFFFFFF),
			m_max_y(-0x7FFFFFFF),
			m_cur_scanline(0)
		{
			m_fake_scanline.y = 0;
			m_fake_scanline.num_spans = 0;
			m_fake_scanline.start_span = 0;
			m_fake_span.x = 0;
			m_fake_span.len = 0;
			m_fake_span.covers_id = 0;
		}

		// Renderer Interface
		//---------------------------------------------------------------
		void prepare()
		{
			m_covers.remove_all();
			m_scanlines.remove_all();
			m_spans.remove_all();
			m_min_x =  0x7FFFFFFF;
			m_min_y =  0x7FFFFFFF;
			m_max_x = -0x7FFFFFFF;
			m_max_y = -0x7FFFFFFF;
			m_cur_scanline = 0;
		}

		//---------------------------------------------------------------
		template<class Scanline> void render(const Scanline& sl)
		{
			scanline_data sl_this;

			int y = sl.y();
			if(y < m_min_y) m_min_y = y;
			if(y > m_max_y) m_max_y = y;

			sl_this.y = y;
			sl_this.num_spans = sl.num_spans();
			sl_this.start_span = m_spans.size();
			typename Scanline::const_iterator span_iterator = sl.begin();

			unsigned num_spans = sl_this.num_spans;
			for(;;)
			{
				span_data sp;

				sp.x		 = span_iterator->x;
				sp.len		 = span_iterator->len;
				int len		 = abs(int(sp.len));
				sp.covers_id =
					m_covers.add_cells(span_iterator->covers,
									   (unsigned(len)));
				m_spans.add(sp);
				int x1 = sp.x;
				int x2 = sp.x + len/3 - 1;
				if(x1 < m_min_x) m_min_x = x1;
				if(x2 > m_max_x) m_max_x = x2;
				if(--num_spans == 0) break;
				++span_iterator;
			}
			m_scanlines.add(sl_this);
		}


		//---------------------------------------------------------------
		// Iterate scanlines interface
		int min_x() const { return m_min_x; }
		int min_y() const { return m_min_y; }
		int max_x() const { return m_max_x; }
		int max_y() const { return m_max_y; }

		//---------------------------------------------------------------
		bool rewind_scanlines()
		{
			m_cur_scanline = 0;
			return m_scanlines.size() > 0;
		}


		//---------------------------------------------------------------
		template<class Scanline> bool sweep_scanline(Scanline& sl)
		{
			sl.reset_spans();
			for(;;)
			{
				if(m_cur_scanline >= m_scanlines.size()) return false;
				const scanline_data& sl_this = m_scanlines[m_cur_scanline];

				unsigned num_spans = sl_this.num_spans;
				unsigned span_idx  = sl_this.start_span;
				do
				{
					const span_data& sp = m_spans[span_idx++];
					const T* covers = covers_by_index(sp.covers_id);
					if(sp.len < 0)
					{
						sl.add_span(sp.x, unsigned(-sp.len), *covers);
					}
					else
					{
						sl.add_cells(sp.x, sp.len, covers);
					}
				}
				while(--num_spans);
				++m_cur_scanline;
				if(sl.num_spans())
				{
					sl.finalize(sl_this.y);
					break;
				}
			}
			return true;
		}


		//---------------------------------------------------------------
		// Specialization for embedded_scanline
		bool sweep_scanline(embedded_scanline& sl)
		{
			do
			{
				if(m_cur_scanline >= m_scanlines.size()) return false;
				sl.init(m_cur_scanline);
				++m_cur_scanline;
			}
			while(sl.num_spans() == 0);
			return true;
		}

		//---------------------------------------------------------------
		unsigned byte_size() const
		{
			unsigned i;
			unsigned size = sizeof(int32) * 4; // min_x, min_y, max_x, max_y

			for(i = 0; i < m_scanlines.size(); ++i)
			{
				size += sizeof(int32) * 3; // scanline size in bytes

				const scanline_data& sl_this = m_scanlines[i];

				unsigned num_spans = sl_this.num_spans;
				unsigned span_idx  = sl_this.start_span;
				do
				{
					const span_data& sp = m_spans[span_idx++];

					size += sizeof(int32) * 2;				  // X, span_len
					if(sp.len < 0)
					{
						size += sizeof(T);					  // cover
					}
					else
					{
						size += sizeof(T) * unsigned(sp.len); // covers
					}
				}
				while(--num_spans);
			}
			return size;
		}


		//---------------------------------------------------------------
		static void write_int32(int8u* dst, int32 val)
		{
			dst[0] = ((const int8u*)&val)[0];
			dst[1] = ((const int8u*)&val)[1];
			dst[2] = ((const int8u*)&val)[2];
			dst[3] = ((const int8u*)&val)[3];
		}


		//---------------------------------------------------------------
		void serialize(int8u* data) const
		{
			unsigned i;

			write_int32(data, min_x()); // min_x
			data += sizeof(int32);
			write_int32(data, min_y()); // min_y
			data += sizeof(int32);
			write_int32(data, max_x()); // max_x
			data += sizeof(int32);
			write_int32(data, max_y()); // max_y
			data += sizeof(int32);

			for(i = 0; i < m_scanlines.size(); ++i)
			{
				const scanline_data& sl_this = m_scanlines[i];

				int8u* size_ptr = data;
				data += sizeof(int32);	// Reserve space for scanline size in bytes

				write_int32(data, sl_this.y);			 // Y
				data += sizeof(int32);

				write_int32(data, sl_this.num_spans);	 // num_spans
				data += sizeof(int32);

				unsigned num_spans = sl_this.num_spans;
				unsigned span_idx  = sl_this.start_span;
				do
				{
					const span_data& sp = m_spans[span_idx++];
					const T* covers = covers_by_index(sp.covers_id);

					write_int32(data, sp.x);			// X
					data += sizeof(int32);

					write_int32(data, sp.len);			// span_len
					data += sizeof(int32);

					if(sp.len < 0)
					{
						memcpy(data, covers, sizeof(T));
						data += sizeof(T);
					}
					else
					{
						memcpy(data, covers, unsigned(sp.len) * sizeof(T));
						data += sizeof(T) * unsigned(sp.len);
					}
				}
				while(--num_spans);
				write_int32(size_ptr, int32(unsigned(data - size_ptr)));
			}
		}


		//---------------------------------------------------------------
		const scanline_data& scanline_by_index(unsigned i) const
		{
			return (i < m_scanlines.size()) ? m_scanlines[i] : m_fake_scanline;
		}

		//---------------------------------------------------------------
		const span_data& span_by_index(unsigned i) const
		{
			return (i < m_spans.size()) ? m_spans[i] : m_fake_span;
		}

		//---------------------------------------------------------------
		const T* covers_by_index(int i) const
		{
			return m_covers[i];
		}

	private:
		scanline_cell_storage<T>	  m_covers;
		pod_bvector<span_data, 10>	  m_spans;
		pod_bvector<scanline_data, 8> m_scanlines;
		span_data	  m_fake_span;
		scanline_data m_fake_scanline;
		int			  m_min_x;
		int			  m_min_y;
		int			  m_max_x;
		int			  m_max_y;
		unsigned	  m_cur_scanline;
	};


	typedef scanline_storage_subpix<int8u>	scanline_storage_subpix8;  //--------scanline_storage_subpix8
	typedef scanline_storage_subpix<int16u> scanline_storage_subpix16; //--------scanline_storage_subpix16
	typedef scanline_storage_subpix<int32u> scanline_storage_subpix32; //--------scanline_storage_subpix32


	//--------------------------------------serialized_scanlines_adaptor_subpix
	template<class T> class serialized_scanlines_adaptor_subpix
	{
	public:
		typedef T cover_type;

		//---------------------------------------------------------------------
		class embedded_scanline
		{
		public:
			typedef T cover_type;

			//-----------------------------------------------------------------
			class const_iterator
			{
			public:
				struct span
				{
					int32	 x;
					int32	 len; // If negative, it's a solid span, "covers" is valid
					const T* covers;
				};

				const_iterator() : m_ptr(0) {}
				const_iterator(const embedded_scanline& sl) :
					m_ptr(sl.m_ptr),
					m_dx(sl.m_dx)
				{
					init_span();
				}

				const span& operator*()	 const { return m_span;	 }
				const span* operator->() const { return &m_span; }

				void operator ++ ()
				{
					if(m_span.len < 0)
					{
						m_ptr += sizeof(T);
					}
					else
					{
						m_ptr += m_span.len * sizeof(T);
					}
					init_span();
				}

			private:
				int read_int32()
				{
					int32 val;
					((int8u*)&val)[0] = *m_ptr++;
					((int8u*)&val)[1] = *m_ptr++;
					((int8u*)&val)[2] = *m_ptr++;
					((int8u*)&val)[3] = *m_ptr++;
					return val;
				}

				void init_span()
				{
					m_span.x	  = read_int32() + m_dx;
					m_span.len	  = read_int32();
					m_span.covers = m_ptr;
				}

				const int8u* m_ptr;
				span		 m_span;
				int			 m_dx;
			};

			friend class const_iterator;


			//-----------------------------------------------------------------
			embedded_scanline() : m_ptr(0), m_y(0), m_num_spans(0) {}

			//-----------------------------------------------------------------
			void	 reset(int, int)	 {}
			unsigned num_spans()   const { return m_num_spans;	}
			int		 y()		   const { return m_y;			}
			const_iterator begin() const { return const_iterator(*this); }


		private:
			//-----------------------------------------------------------------
			int read_int32()
			{
				int32 val;
				((int8u*)&val)[0] = *m_ptr++;
				((int8u*)&val)[1] = *m_ptr++;
				((int8u*)&val)[2] = *m_ptr++;
				((int8u*)&val)[3] = *m_ptr++;
				return val;
			}

		public:
			//-----------------------------------------------------------------
			void init(const int8u* ptr, int dx, int dy)
			{
				m_ptr		= ptr;
				m_y			= read_int32() + dy;
				m_num_spans = unsigned(read_int32());
				m_dx		= dx;
			}

		private:
			const int8u* m_ptr;
			int			 m_y;
			unsigned	 m_num_spans;
			int			 m_dx;
		};



	public:
		//--------------------------------------------------------------------
		serialized_scanlines_adaptor_subpix() :
			m_data(0),
			m_end(0),
			m_ptr(0),
			m_dx(0),
			m_dy(0),
			m_min_x(0x7FFFFFFF),
			m_min_y(0x7FFFFFFF),
			m_max_x(-0x7FFFFFFF),
			m_max_y(-0x7FFFFFFF)
		{}

		//--------------------------------------------------------------------
		serialized_scanlines_adaptor_subpix(const int8u* data, unsigned size,
										double dx, double dy) :
			m_data(data),
			m_end(data + size),
			m_ptr(data),
			m_dx(iround(dx)),
			m_dy(iround(dy)),
			m_min_x(0x7FFFFFFF),
			m_min_y(0x7FFFFFFF),
			m_max_x(-0x7FFFFFFF),
			m_max_y(-0x7FFFFFFF)
		{}

		//--------------------------------------------------------------------
		void init(const int8u* data, unsigned size, double dx, double dy)
		{
			m_data	= data;
			m_end	= data + size;
			m_ptr	= data;
			m_dx	= iround(dx);
			m_dy	= iround(dy);
			m_min_x = 0x7FFFFFFF;
			m_min_y = 0x7FFFFFFF;
			m_max_x = -0x7FFFFFFF;
			m_max_y = -0x7FFFFFFF;
		}

	private:
		//--------------------------------------------------------------------
		int read_int32()
		{
			int32 val;
			((int8u*)&val)[0] = *m_ptr++;
			((int8u*)&val)[1] = *m_ptr++;
			((int8u*)&val)[2] = *m_ptr++;
			((int8u*)&val)[3] = *m_ptr++;
			return val;
		}

		//--------------------------------------------------------------------
		unsigned read_int32u()
		{
			int32u val;
			((int8u*)&val)[0] = *m_ptr++;
			((int8u*)&val)[1] = *m_ptr++;
			((int8u*)&val)[2] = *m_ptr++;
			((int8u*)&val)[3] = *m_ptr++;
			return val;
		}

	public:
		// Iterate scanlines interface
		//--------------------------------------------------------------------
		bool rewind_scanlines()
		{
			m_ptr = m_data;
			if(m_ptr < m_end)
			{
				m_min_x = read_int32() + m_dx;
				m_min_y = read_int32() + m_dy;
				m_max_x = read_int32() + m_dx;
				m_max_y = read_int32() + m_dy;
			}
			return m_ptr < m_end;
		}

		//--------------------------------------------------------------------
		int min_x() const { return m_min_x; }
		int min_y() const { return m_min_y; }
		int max_x() const { return m_max_x; }
		int max_y() const { return m_max_y; }

		//--------------------------------------------------------------------
		template<class Scanline> bool sweep_scanline(Scanline& sl)
		{
			sl.reset_spans();
			for(;;)
			{
				if(m_ptr >= m_end) return false;

				read_int32();	   // Skip scanline size in bytes
				int y = read_int32() + m_dy;
				unsigned num_spans = read_int32();

				do
				{
					int x = read_int32() + m_dx;
					int len = read_int32();

					if(len < 0)
					{
						sl.add_span(x, unsigned(-len), *m_ptr);
						m_ptr += sizeof(T);
					}
					else
					{
						sl.add_cells(x, len, m_ptr);
						m_ptr += len * sizeof(T);
					}
				}
				while(--num_spans);

				if(sl.num_spans())
				{
					sl.finalize(y);
					break;
				}
			}
			return true;
		}


		//--------------------------------------------------------------------
		// Specialization for embedded_scanline
		bool sweep_scanline(embedded_scanline& sl)
		{
			do
			{
				if(m_ptr >= m_end) return false;

				unsigned byte_size = read_int32u();
				sl.init(m_ptr, m_dx, m_dy);
				m_ptr += byte_size - sizeof(int32);
			}
			while(sl.num_spans() == 0);
			return true;
		}

	private:
		const int8u* m_data;
		const int8u* m_end;
		const int8u* m_ptr;
		int			 m_dx;
		int			 m_dy;
		int			 m_min_x;
		int			 m_min_y;
		int			 m_max_x;
		int			 m_max_y;
	};



	typedef serialized_scanlines_adaptor_subpix<int8u>
	serialized_scanlines_adaptor_subpix8; //serialized_scanlines_adaptor_subpix8

	typedef serialized_scanlines_adaptor_subpix<int16u>
	serialized_scanlines_adaptor_subpix16; //serialized_scanlines_adaptor_subpix16

	typedef serialized_scanlines_adaptor_subpix<int32u>
	serialized_scanlines_adaptor_subpix32; //serialized_scanlines_adaptor_subpix32

}


#endif

